#include "stdafx.h"

#include "game.h"

#define WIDTH  1920
#define HEIGHT 1080
 
// Windows handles
HDC       hDC  = NULL;
HGLRC     hRC  = NULL;
HWND      hWnd = NULL;
HINSTANCE hInstance;
 
// Array containing all keydown and keyups
bool gKeys[256];
 
// Window parameters
bool gActive     = TRUE;
bool gFullscreen = TRUE;

// Rendersurface, might need to add some kind of wrapper class for menu's and stuff
RenderSurface *gRenderSurface = new Game();
 
// Definition of window proc callback
LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM );

#ifdef _DEBUG
void RedirectIOToConsole()
{
    using namespace std;

    int hConHandle;
    long lStdHandle;

    CONSOLE_SCREEN_BUFFER_INFO coninfo;

    FILE *fp;

    AllocConsole();

    GetConsoleScreenBufferInfo( GetStdHandle( STD_OUTPUT_HANDLE ), &coninfo );

    coninfo.dwSize.Y = 500;

    SetConsoleScreenBufferSize( GetStdHandle( STD_OUTPUT_HANDLE ), coninfo.dwSize );

    lStdHandle = (long) GetStdHandle( STD_OUTPUT_HANDLE );
    hConHandle = _open_osfhandle( lStdHandle, _O_TEXT );

    fp = _fdopen( hConHandle, "w" );

    *stdout = *fp;

    setvbuf( stdout, NULL, _IONBF, 0 );

    lStdHandle = (long) GetStdHandle( STD_INPUT_HANDLE );
    hConHandle = _open_osfhandle( lStdHandle, _O_TEXT );

    fp = _fdopen( hConHandle, "r" );

    *stdin = *fp;

    setvbuf( stdin, NULL, _IONBF, 0 );

    lStdHandle = (long) GetStdHandle( STD_ERROR_HANDLE );
    hConHandle = _open_osfhandle( lStdHandle, _O_TEXT );

    fp = _fdopen( hConHandle, "w" );

    *stderr = *fp;

    setvbuf( stderr, NULL, _IONBF, 0 );

    ios::sync_with_stdio();
}
#endif

void RedirectErrorStream( void )
{
    std::ofstream errorFile( "error.log" ); 
    std::cerr.rdbuf( errorFile.rdbuf() ); 
}

int ValidateDeviL( void )
{
    if( ilGetInteger(IL_VERSION_NUM) < IL_VERSION || iluGetInteger( ILU_VERSION_NUM ) < ILU_VERSION || ilutGetInteger(ILUT_VERSION_NUM) < ILUT_VERSION ) 
    {
        std::cerr << "Error: Invalid DeviL versions" << std::endl;

        return FALSE;
    }

    return TRUE;
}
 
int InitGL( GLvoid )
{
    glEnable( GL_TEXTURE_2D );
    glShadeModel( GL_SMOOTH );
    glClearColor( 0.0f, 0.0f, 0.0f, 0.5f );
    glClearDepth( 1.0f );
    glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );

    ilInit();
    iluInit();
    ilutInit();
    ilutRenderer( ILUT_OPENGL );

    World::Init();

	gRenderSurface->Init();
 
    return TRUE;
}
 
int DrawGLScene( GLvoid )
{
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    
	gRenderSurface->Render();

    return TRUE;
}
 
GLvoid KillGLWindow( GLvoid )
{
    if( gFullscreen )
    {
        ChangeDisplaySettings( NULL, 0 );
        ShowCursor( TRUE );
    }
 
    if( hRC )
    {
        if( !wglMakeCurrent( NULL, NULL ) )
        {
            std::cerr << "Error: Release of DC and RC failed";
        }
 
        if( !wglDeleteContext( hRC ) )
        {
            std::cerr << "Error: Release rendering context failed";
        }

        hRC = NULL;
    }
 
    if( hDC && !ReleaseDC( hWnd, hDC ) )
    {
        std::cerr << "Error: Release device context failed";
        hDC = NULL;
    }
 
    if( hWnd && !DestroyWindow( hWnd ) )
    {
        std::cerr << "Error: Could not release hWnd";
        hWnd = NULL;
    }
 
    if( !UnregisterClass( "OpenGL", hInstance ) )
    {
        std::cerr << "Error: Could not unregister class";
        hInstance = NULL;
    }
}

BOOL CreateGLWindow( char* pTitle, const int pWidth, const int pHeight, const int pBits, const bool pgFullscreenflag )
{
    GLuint      PixelFormat;
    WNDCLASS    wc;
    DWORD       dwExStyle;
    DWORD       dwStyle;
    RECT        WindowRect;

    WindowRect.left   = (long) 0;
    WindowRect.right  = (long) pWidth;
    WindowRect.top    = (long) 0;
    WindowRect.bottom = (long) pHeight;
 
    gFullscreen = pgFullscreenflag;
 
    hInstance           = GetModuleHandle( NULL );
    wc.style            = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    wc.lpfnWndProc      = (WNDPROC) WndProc;
    wc.cbClsExtra       = 0;
    wc.cbWndExtra       = 0;
    wc.hInstance        = hInstance;
    wc.hIcon            = LoadIcon( NULL, IDI_WINLOGO );
    wc.hCursor          = LoadCursor( NULL, IDC_ARROW );
    wc.hbrBackground    = NULL;
    wc.lpszMenuName     = NULL;
    wc.lpszClassName    = "OpenGL";
 
    if( !RegisterClass( &wc ) )
    {
        std::cerr << "Error: Failed to register the window class";

        return FALSE;
    }
     
    if( gFullscreen )
    {
        DEVMODE dmScreenSettings;
        memset( &dmScreenSettings, 0, sizeof( dmScreenSettings ) );

        dmScreenSettings.dmSize       = sizeof(dmScreenSettings);       
        dmScreenSettings.dmPelsWidth  = pWidth;
        dmScreenSettings.dmPelsHeight = pHeight;
        dmScreenSettings.dmBitsPerPel = pBits;
        dmScreenSettings.dmFields     = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
 
        if( ChangeDisplaySettings( &dmScreenSettings, CDS_FULLSCREEN ) != DISP_CHANGE_SUCCESSFUL )
        {
            std::cerr << "Error: The requested fullscreen mode is not supported by your videocard";

            gFullscreen = FALSE;
        }
    }
 
    if( gFullscreen )
    {
        dwExStyle = WS_EX_APPWINDOW;
        dwStyle   = WS_POPUP;

        ShowCursor( FALSE );
    }
    else
    {
        dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
        dwStyle   = WS_OVERLAPPEDWINDOW;
    }
 
    AdjustWindowRectEx( &WindowRect, dwStyle, FALSE, dwExStyle );
 
    if( !(hWnd = CreateWindowEx( dwExStyle,
                                 "OpenGL",                         // Class Name
                                 pTitle,                            // Window Title
                                 dwStyle |                         // Defined Window Style
                                 WS_CLIPSIBLINGS |                 // Required Window Style
                                 WS_CLIPCHILDREN,                  // Required Window Style
                                 0, 0,                             // Window Position
                                 WindowRect.right-WindowRect.left, // Calculate Window Width
                                 WindowRect.bottom-WindowRect.top, // Calculate Window Height
                                 NULL,                             // No Parent Window
                                 NULL,                             // No Menu
                                 hInstance,                        // Instance
                                 NULL )) )                         // Dont Pass Anything To WM_CREATE
    {
        KillGLWindow();
        std::cerr << "Error: Window creation error";

        return FALSE;
    }
 
    static  PIXELFORMATDESCRIPTOR pfd =
    {
        sizeof( PIXELFORMATDESCRIPTOR ), // Size Of This Pixel Format Descriptor
        1,                               // Version Number
        PFD_DRAW_TO_WINDOW |             // Format Must Support Window
        PFD_SUPPORT_OPENGL |             // Format Must Support OpenGL
        PFD_DOUBLEBUFFER,                // Must Support Double Buffering
        PFD_TYPE_RGBA,                   // Request An RGBA Format
        pBits,                            // Select Our Color Depth
        0, 0, 0, 0, 0, 0,                // Color Bits Ignored
        0,                               // No Alpha Buffer
        0,                               // Shift Bit Ignored
        0,                               // No Accumulation Buffer
        0, 0, 0, 0,                      // Accumulation Bits Ignored
        16,                              // 16Bit Z-Buffer (Depth Buffer)  
        0,                               // No Stencil Buffer
        0,                               // No Auxiliary Buffer
        PFD_MAIN_PLANE,                  // Main Drawing Layer
        0,                               // Reserved
        0, 0, 0                          // Layer Masks Ignored
    };
     
    if( !(hDC = GetDC( hWnd )) )
    {
        KillGLWindow();
        std::cerr << "Error: Can't create an OpenGL device context";

        return FALSE;
    }
 
    if( !(PixelFormat = ChoosePixelFormat( hDC, &pfd )) )
    {
        KillGLWindow();
        std::cerr << "Error: Can't find a suitable pixelformat";

        return FALSE;
    }
 
    if( !SetPixelFormat( hDC, PixelFormat, &pfd ) )
    {
        KillGLWindow();
        std::cerr << "Error: Can't set the pixelformat";

        return FALSE;
    }
 
    if( !(hRC = wglCreateContext( hDC )) )
    {
        KillGLWindow();
        std::cerr << "Error: Can't create an OpenGL rendering context";

        return FALSE;
    }
 
    if( !wglMakeCurrent( hDC, hRC ) )
    {
        KillGLWindow();
        std::cerr << "Error: Can't activate the OpenGL rendering context";

        return FALSE;
    }
 
    ShowWindow( hWnd, SW_SHOW );
    SetForegroundWindow( hWnd );
    SetFocus( hWnd );

    glResize( pWidth, pHeight );
 
    if( !InitGL() )
    {
        KillGLWindow();
        std::cerr << "Error: Initialization failed";

        return FALSE;
    }
 
    return TRUE;
}
 
LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    switch( uMsg )
    {
        case WM_ACTIVATE:
        {
            if( !HIWORD( wParam ) )
            {
                gActive = TRUE;
            }
            else
            {
                gActive = FALSE;
            }
        }
 
        case WM_SYSCOMMAND:
        {
            switch( wParam )
            {
                case SC_SCREENSAVE:
                case SC_MONITORPOWER:
                return 0;
            }

            break;
        }
 
        case WM_CLOSE:
        {
            PostQuitMessage( 0 );

            return 0;
        }
 
        case WM_KEYDOWN:
        {
            gKeys[wParam] = TRUE; 

            return 0;
        }
 
        case WM_KEYUP:
        {
            gKeys[wParam] = FALSE;

            return 0;
        }
 
        case WM_SIZE:
        {
            glResize( LOWORD( lParam ), HIWORD( lParam ) );

            return 0;
        }
    }
 
    return DefWindowProc( hWnd, uMsg, wParam, lParam );
}
 
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
#ifdef _DEBUG
    RedirectIOToConsole();
#endif

    RedirectErrorStream();
    ValidateDeviL();

    MSG     msg;
    BOOL    done = FALSE;
    
    gFullscreen = TRUE;
 
    if( !CreateGLWindow( "FluidWars", WIDTH, HEIGHT, 16, gFullscreen ) )
    {
        return 0;
    }
 
    while( !done )
    {
        if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
        {
            if( msg.message == WM_QUIT )
            {
                done = TRUE;
            }
            else
            {
                TranslateMessage( &msg );
                DispatchMessage( &msg );
            }
        }
        else
        {
            if( (gActive && !DrawGLScene()) || gKeys[VK_ESCAPE] ) 
            {
                done = TRUE;
            }
            else
            {
                SwapBuffers( hDC );
 
                if( gKeys[VK_F1] )
                {
                    gKeys[VK_F1] = FALSE;
                    KillGLWindow();
                    gFullscreen = !gFullscreen;

                    if( !CreateGLWindow( "FluidWars", WIDTH, HEIGHT, 16, gFullscreen ) )
                    {
                        return 0;
                    }
                }

                gRenderSurface->HandleInput( gKeys );
            }
        }
    }
 
    KillGLWindow();

    return msg.wParam;
}
